export function isDate(value) {
  return Object.prototype.toString.call(value) === '[object Date]';
}

export function isRegExp(value) {
  return Object.prototype.toString.call(value) === '[object RegExp]';
}

export function isWindow(obj) {
  return obj && obj.window === obj;
}

export function isFunction(value) {
  return typeof value === 'function';
}

export function isDefined(value) {
  return typeof value !== 'undefined';
}

export function equals(o1, o2) {
  if (o1 === o2) {
      return true
  };
  if (o1 === null || o2 === null) {
      return false;
  }
  if (o1 !== o1 && o2 !== o2) {
      return true; // NaN === NaN
  }
  const t1 = typeof o1;
  const t2 = typeof o2;
  let length;
  let key;
  let keySet;
  if (t1 === t2 && t1 === 'object') {
      if (Array.isArray(o1)) {
          if (!Array.isArray(o2)) {
              return false;
          }
          if ((length = o1.length) === o2.length) {
              for (key = 0; key < length; key++) {
                  if (!equals(o1[key], o2[key])) {
                      return false;
                  }
              }
              return true;
          }
      }
      else if (isDate(o1)) {
          if (!isDate(o2)) {
              return false;
          }
          return equals(o1.getTime(), o2.getTime());
      }
      else if (isRegExp(o1)) {
          if (!isRegExp(o2)) {
              return false;
          }
          return o1.toString() === o2.toString();
      }
      else {
          if (isWindow(o1) || isWindow(o2)
              || Array.isArray(o2) || isDate(o2) || isRegExp(o2)
          ) {
              return false
          };
          keySet = Object.create(null);
          for (key in o1) {
              if (key.charAt(0) === '$' || isFunction(o1[key])) {
                  continue
              };
              if (!equals(o1[key], o2[key])) {
                  return false;
              }
              keySet[key] = true;
          }
          for (key in o2) {
              if (!(key in keySet)
                  && key.charAt(0) !== '$'
                  && isDefined(o2[key])
                  && !isFunction(o2[key])
              ) {
                  return false;
              }
          }
          return true;
      }
  }
  return false;
}
